import java.net.URI;
import java.io.*;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.Executors;
import java.security.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class tracker
{
	public static void main(String[] args) throws IOException
	{
		InetSocketAddress addr = new InetSocketAddress(8080);
		HttpServer server = HttpServer.create(addr, 0);

		server.createContext("/", new MyHandler());
		server.setExecutor(Executors.newCachedThreadPool());
		server.start();
		System.out.println("Server : "+addr);
	}
}

class MyHandler implements HttpHandler
{
	public static ConcurrentHashMap<Integer, Status> status = new ConcurrentHashMap<Integer, Status>();
	public static ConcurrentHashMap<String, Owner> file = new ConcurrentHashMap<String, Owner>();
	public static ConcurrentHashMap<String, String> hash = new ConcurrentHashMap<String, String>();
	
	public void handle(HttpExchange exchange) throws IOException
	{
		Status temp;
		Owner temp2;
		String requestMethod = exchange.getRequestMethod(),  URI =
		exchange.getRequestURI().toString(), local = exchange.getLocalAddress().toString(), remote = exchange.getRemoteAddress().toString();
		Integer[] I_P;
		Integer IP, online, i, j, k;
		String[] value = new String[6];
		System.out.println(requestMethod);
		System.out.println("URL = "+local+URI);
		long waktu = System.currentTimeMillis();
		
		if (requestMethod.equalsIgnoreCase("GET"))
		{
			I_P = Parse(remote);
			System.out.println("IPnya = "+((I_P[0]>>24)&255)+" "+((I_P[0]>>16)&255)+" "+((I_P[0]>>8)&255)+" "+(I_P[0]&255)+" "+", portnya = "+I_P[1]);
			Vector seeders;
			OutputStream responseBody = exchange.getResponseBody();
			OutputStreamWriter osw = new OutputStreamWriter(responseBody);
			Headers requestHeaders = exchange.getRequestHeaders();
			Headers responseHeaders = exchange.getResponseHeaders();
			responseHeaders.set("Content-Type", "text/plain");
			exchange.sendResponseHeaders(200, 0);
			Set<String> keySet = requestHeaders.keySet();
			Iterator<String> iter = keySet.iterator();
			
			while (iter.hasNext())
			{
				String key = iter.next();
				List values = requestHeaders.get(key);
				// String s = key + " = " + values.toString() + "\n";
				// responseBody.write(s.getBytes());
			}
			value = getValue(URI);
			if (!status.containsKey(I_P[0]))
			{
				temp = new Status(I_P[1], waktu, 180);
				if (value[5]!=null)
					status.put(I_P[0], temp);
				if (value[5].equals("started"))
					status.put(I_P[0], temp);
				else
					osw.write("failure_reason seharusnya start dulu\n");
			}
			else
			{
				temp = status.get(I_P[0]);
				temp.last_msg = waktu;
				temp.port = I_P[1];
				temp.interval = 180*1000;
				if (temp.event==2)
				{
					if (value[5].equals("started") || value[5] == null)
						temp.event = 0;
					else
						osw.write("failure_reason seharusnya start dulu\n");
				}
				status.put(I_P[0], temp);
			}
			System.out.println(".......");
			osw.write("warning warning\n");
			osw.write("interval 180\n");
			if (file.containsKey(value[0]))
			{
				temp2 = file.get(value[0]);
				seeders = temp2.get_seeder();
				online = 0;
				for (Map.Entry<Integer, Status> entry : status.entrySet())
				{
					IP = entry.getKey();
					temp = entry.getValue();
					if (temp.last_msg + temp.interval > waktu)
					{
						++online;
						temp.online = 1;
					}
					else
						temp.online = 0;
					entry.setValue(temp);
					System.out.println(temp.last_msg+", "+temp.interval+"..."+waktu);
				}
				for (i=seeders.size(); i>0;)
				{
					--i;
					if (status.get(seeders.elementAt(i)).online==0)
						seeders.removeElementAt(i);
				}
				osw.write("complete "+seeders.size()+"\n");
				osw.write("incomplete "+(online-seeders.size())+"\n");
				osw.write("peers ");
				for (i=seeders.size(); i>0;)
				{
					--i;
					j = (Integer)seeders.elementAt(i);
					k = status.get(j).port;
					osw.write(""+((j>>24)&255)+"."+((j>>16)&255)+"."+((j>>8)&255)+"."+(j&255)+":"+(k)+"\n...");
				}
			}
			else
			{
				temp2 = new Owner();
				osw.write("complete 0\n");
				osw.write("incomplete 0\n");
				osw.write("peers ");
			}
			osw.close();
			responseBody.close();
			if (value[4].equals("0"))
			{
				if (hash.containsKey(value[0]))
				{
					temp2.add_owner(I_P[0]);
					file.put(value[0], temp2);
				}
				else
				{
					hash.put(value[0], SHA1(value[0]));
					temp2 = new Owner(I_P[0]);
					file.put(value[0], temp2);
				}
			}
		}
	}
	
	private String[] getValue(String s)
	{
		String[] result = new String[6];
		String para;
		int i, j, k=2, l=0, m = s.length();
		for (i=2; i<m; ++i)
		{
			if (s.charAt(i)=='&')
			{
				para = s.substring(k,l);
				
				if (para.equals("file_name"))
					result[0] = s.substring(l+1,i);
				else if (para.equals("port"))
					result[1] = s.substring(l+1,i);
				else if (para.equals("uploaded"))
					result[2] = s.substring(l+1,i);
				else if (para.equals("downloaded"))
					result[3] = s.substring(l+1,i);
				else if (para.equals("left"))
					result[4] = s.substring(l+1,i);
				else if (para.equals("event"))
					result[5] = s.substring(l+1,i);
				k = i+1;
			}
			else if (s.charAt(i)=='=')
				l = i;
		}
		para = s.substring(k,l);
		if (para.equals("file_name"))
			result[0] = s.substring(l+1,i);
		else if (para.equals("port"))
			result[1] = s.substring(l+1,i);
		else if (para.equals("uploaded"))
			result[2] = s.substring(l+1,i);
		else if (para.equals("downloaded"))
			result[3] = s.substring(l+1,i);
		else if (para.equals("left"))
			result[4] = s.substring(l+1,i);
		else if (para.equals("event"))
			result[5] = s.substring(l+1,i);
		return result;
	}
	
	public static String SHA1(String input)
	{
		try {
			MessageDigest md = MessageDigest.getInstance("SHA1");
			md.update(input.getBytes()); 
			byte[] output = md.digest();
			return bytesToHex(output);
		}
		catch (Exception e) {
			System.out.println("Exception: "+e);
			return "Exception: "+e;
		}
	}
	
	public static String bytesToHex(byte[] b)
	{
		char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7',
						   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
		StringBuffer buf = new StringBuffer();
		for (int j=0; j<b.length; j++)
		{
			buf.append(hexDigit[(b[j] >> 4) & 0x0f]);
			buf.append(hexDigit[b[j] & 0x0f]);
		}
		return buf.toString();
	}
	
	public static String bytesToHex(String b)
	{
		char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7',
						   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
		StringBuffer buf = new StringBuffer();
		int i;
		for (int j=0; j<b.length(); j++)
		{
			i = (int)b.charAt(j);
			buf.append(hexDigit[(i >> 4) & 0x0f]);
			buf.append(hexDigit[i & 0x0f]);
		}
		return buf.toString();
	}
	
	public Integer[] Parse(String s)
	{
		int i=0, length = s.length(), j=0, k=0;
		Integer[] result = {0,0};
		char c;
		while ((++i<length) && (k<4))
		{
			c = s.charAt(i);
			if (c=='.' || c==':')
			{
				result[0] = (result[0] << 8) | j;
				j=0;
				++k;
			}
			else
				j = j*10+(int)c-48;
		}
		
		while (i<length)
		{
			c = s.charAt(i);
			j = j*10+(int)c-48;
			++i;
		}
		result[1] = j;
		return result;
	}
}

class Status
{
	public int event, uploaded, downloaded, left, port, online;
	long last_msg, interval;
	
	public Status()
	{
		event = uploaded = downloaded = left = 0;
		last_msg = interval = 0;
		online = 1;
		port = 8080;
	}
	
	public Status(int Port, long Last, int Interval)
	{
		event= uploaded= downloaded= left=0;
		port = Port;
		last_msg = Last;
		online = 0;
		interval = Interval*1000;
	}
}

class Owner
{
	TreeSet<Integer> owner;
	
	public Owner()
	{
		owner = new TreeSet<Integer>();
	}
	
	public Owner(Integer s)
	{
		owner = new TreeSet<Integer>();
		owner.add(s);
	}
	
	Vector<Integer> get_seeder()
	{
		//long waktu = System.currentTimeMillis();
		Iterator itr = owner.iterator();
		Vector<Integer> seeders = new Vector<Integer>();
		int length = owner.size();
		
		while (itr.hasNext())
			seeders.add((Integer)itr.next());
		return seeders;
	}
	
	public void add_owner(Integer s)
	{
		if (!owner.contains(s))
			owner.add(s);
	}
}
